import { Client } from "../src/mod.ts";
import { User } from "../src/user.ts";

/**
 * State type for issues and PRs
 */
export type StateType = "open" | "closed";

/**
 * Label represents a label
 */
export interface Label {
  id: number;
  name: string;
  color: string;
  url: string;
}

/**
 * Milestone represents a milestone of repository.
 */
export interface Milestone {
  id: number;
  title: string;
  description: string;
  state: StateType;
  open_issues: number;
  closed_issues: number;
  closed_at?: string;
  due_on?: string;
}

/**
 * PullRequestMeta represents the meta information of a pull request.
 */
export interface PullRequestMeta {
  merged: boolean;
  merged_at?: string;
}

/**
 * Comment represents a comment in commit and issue page.
 */
export interface Comment {
  id: number;
  html_url: string;
  user: User;
  body: string;
  created_at: string;
  updated_at: string;
}

/**
 * Issue represents an issue of a repository
 */
export interface Issue {
  id: number;
  number: number;
  user: User;
  title: string;
  body: string;
  labels: Label[];
  milestone?: Milestone;
  assignee?: User;
  state: StateType;
  comments: number;
  created_at: string;
  updated_at: string;
  pull_request?: PullRequestMeta;
}

/**
 * Options when listing issues
 */
export interface ListIssueOption {
  page?: number;
  state?: string;
}

/**
 * Options for creating an issue
 */
export interface CreateIssueOption {
  title: string;
  body?: string;
  assignee?: string;
  milestone?: number;
  labels?: number[];
  closed?: boolean;
}

/**
 * Options for editing an issue
 */
export interface EditIssueOption {
  title?: string;
  body?: string;
  assignee?: string;
  milestone?: number;
  state?: string;
}

/**
 * Options for creating a label
 */
export interface CreateLabelOption {
  name: string;
  color: string;
}

/**
 * Options for editing a label
 */
export interface EditLabelOption {
  name?: string;
  color?: string;
}

/**
 * Options for issue labels
 */
export interface IssueLabelsOption {
  labels: number[];
}

/**
 * Options for creating a milestone
 */
export interface CreateMilestoneOption {
  title: string;
  description?: string;
  due_on?: string;
}

/**
 * Options for editing a milestone
 */
export interface EditMilestoneOption {
  title?: string;
  description?: string;
  state?: string;
  due_on?: string;
}

/**
 * Options for creating a comment
 */
export interface CreateIssueCommentOption {
  body: string;
}

/**
 * Options for editing a comment
 */
export interface EditIssueCommentOption {
  body: string;
}

/**
 * Issue API endpoints
 */
export class IssueAPI {
  constructor(private client: Client) {}

  /**
   * List all issues
   */
  async listIssues(options: ListIssueOption = {}): Promise<Issue[]> {
    const { page = 1, state = "open" } = options;
    return await this.client.doRequest<Issue[]>("GET", `/issues?page=${page}&state=${state}`);
  }

  /**
   * List issues for the authenticated user
   */
  async listUserIssues(options: ListIssueOption = {}): Promise<Issue[]> {
    const { page = 1, state = "open" } = options;
    return await this.client.doRequest<Issue[]>("GET", `/user/issues?page=${page}&state=${state}`);
  }

  /**
   * List issues for a repository
   */
  async listRepoIssues(owner: string, repo: string, options: ListIssueOption = {}): Promise<Issue[]> {
    const { page = 1, state = "open" } = options;
    return await this.client.doRequest<Issue[]>("GET", `/repos/${owner}/${repo}/issues?page=${page}&state=${state}`);
  }

  /**
   * Get an issue by ID
   */
  async getIssue(owner: string, repo: string, index: number): Promise<Issue> {
    return await this.client.doRequest<Issue>("GET", `/repos/${owner}/${repo}/issues/${index}`);
  }

  /**
   * Create an issue
   */
  async createIssue(owner: string, repo: string, options: CreateIssueOption): Promise<Issue> {
    return await this.client.doRequest<Issue>("POST", `/repos/${owner}/${repo}/issues`, {
      "Content-Type": "application/json",
    }, options);
  }

  /**
   * Edit an issue
   */
  async editIssue(owner: string, repo: string, index: number, options: EditIssueOption): Promise<Issue> {
    return await this.client.doRequest<Issue>("PATCH", `/repos/${owner}/${repo}/issues/${index}`, {
      "Content-Type": "application/json",
    }, options);
  }

  // Comment API

  /**
   * List comments on an issue
   */
  async listIssueComments(owner: string, repo: string, index: number): Promise<Comment[]> {
    return await this.client.doRequest<Comment[]>(
      "GET", 
      `/repos/${owner}/${repo}/issues/${index}/comments`
    );
  }

  /**
   * List comments in a repository
   */
  async listRepoIssueComments(owner: string, repo: string): Promise<Comment[]> {
    return await this.client.doRequest<Comment[]>(
      "GET", 
      `/repos/${owner}/${repo}/issues/comments`
    );
  }

  /**
   * Create a comment on an issue
   */
  async createIssueComment(owner: string, repo: string, index: number, options: CreateIssueCommentOption): Promise<Comment> {
    return await this.client.doRequest<Comment>(
      "POST", 
      `/repos/${owner}/${repo}/issues/${index}/comments`, 
      { "Content-Type": "application/json" },
      options
    );
  }

  /**
   * Edit a comment
   */
  async editIssueComment(owner: string, repo: string, index: number, commentID: number, options: EditIssueCommentOption): Promise<Comment> {
    return await this.client.doRequest<Comment>(
      "PATCH", 
      `/repos/${owner}/${repo}/issues/${index}/comments/${commentID}`, 
      { "Content-Type": "application/json" },
      options
    );
  }

  /**
   * Delete a comment
   */
  async deleteIssueComment(owner: string, repo: string, index: number, commentID: number): Promise<void> {
    await this.client.doRequest<void>(
      "DELETE", 
      `/repos/${owner}/${repo}/issues/${index}/comments/${commentID}`
    );
  }

  // Label API
  
  /**
   * List all labels of a repository
   */
  async listRepoLabels(owner: string, repo: string): Promise<Label[]> {
    return await this.client.doRequest<Label[]>("GET", `/repos/${owner}/${repo}/labels`);
  }

  /**
   * Get a label by ID
   */
  async getRepoLabel(owner: string, repo: string, id: number): Promise<Label> {
    return await this.client.doRequest<Label>("GET", `/repos/${owner}/${repo}/labels/${id}`);
  }

  /**
   * Create a label
   */
  async createLabel(owner: string, repo: string, options: CreateLabelOption): Promise<Label> {
    return await this.client.doRequest<Label>("POST", `/repos/${owner}/${repo}/labels`, {
      "Content-Type": "application/json",
    }, options);
  }

  /**
   * Edit a label
   */
  async editLabel(owner: string, repo: string, id: number, options: EditLabelOption): Promise<Label> {
    return await this.client.doRequest<Label>("PATCH", `/repos/${owner}/${repo}/labels/${id}`, {
      "Content-Type": "application/json",
    }, options);
  }

  /**
   * Delete a label
   */
  async deleteLabel(owner: string, repo: string, id: number): Promise<void> {
    await this.client.doRequest<void>("DELETE", `/repos/${owner}/${repo}/labels/${id}`);
  }

  /**
   * Get labels of an issue
   */
  async getIssueLabels(owner: string, repo: string, index: number): Promise<Label[]> {
    return await this.client.doRequest<Label[]>("GET", `/repos/${owner}/${repo}/issues/${index}/labels`);
  }

  /**
   * Add labels to an issue
   */
  async addIssueLabels(owner: string, repo: string, index: number, options: IssueLabelsOption): Promise<Label[]> {
    return await this.client.doRequest<Label[]>("POST", `/repos/${owner}/${repo}/issues/${index}/labels`, {
      "Content-Type": "application/json",
    }, options);
  }

  /**
   * Replace labels of an issue
   */
  async replaceIssueLabels(owner: string, repo: string, index: number, options: IssueLabelsOption): Promise<Label[]> {
    return await this.client.doRequest<Label[]>("PUT", `/repos/${owner}/${repo}/issues/${index}/labels`, {
      "Content-Type": "application/json",
    }, options);
  }

  /**
   * Delete a label from an issue
   */
  async deleteIssueLabel(owner: string, repo: string, index: number, label: number): Promise<void> {
    await this.client.doRequest<void>("DELETE", `/repos/${owner}/${repo}/issues/${index}/labels/${label}`);
  }

  /**
   * Clear all labels from an issue
   */
  async clearIssueLabels(owner: string, repo: string, index: number): Promise<void> {
    await this.client.doRequest<void>("DELETE", `/repos/${owner}/${repo}/issues/${index}/labels`);
  }

  // Milestone API
  
  /**
   * List all milestones of a repository
   */
  async listRepoMilestones(owner: string, repo: string): Promise<Milestone[]> {
    return await this.client.doRequest<Milestone[]>("GET", `/repos/${owner}/${repo}/milestones`);
  }

  /**
   * Get a milestone by ID
   */
  async getMilestone(owner: string, repo: string, id: number): Promise<Milestone> {
    return await this.client.doRequest<Milestone>("GET", `/repos/${owner}/${repo}/milestones/${id}`);
  }

  /**
   * Create a milestone
   */
  async createMilestone(owner: string, repo: string, options: CreateMilestoneOption): Promise<Milestone> {
    return await this.client.doRequest<Milestone>("POST", `/repos/${owner}/${repo}/milestones`, {
      "Content-Type": "application/json",
    }, options);
  }

  /**
   * Edit a milestone
   */
  async editMilestone(owner: string, repo: string, id: number, options: EditMilestoneOption): Promise<Milestone> {
    return await this.client.doRequest<Milestone>("PATCH", `/repos/${owner}/${repo}/milestones/${id}`, {
      "Content-Type": "application/json",
    }, options);
  }

  /**
   * Delete a milestone
   */
  async deleteMilestone(owner: string, repo: string, id: number): Promise<void> {
    await this.client.doRequest<void>("DELETE", `/repos/${owner}/${repo}/milestones/${id}`);
  }
} 